#include <audio/MIDI/MIDIInDeviceMacOSX.h>
#include <system/Log.h>

#if NE_PLATFORM == NE_PLATFORM_MACOSX

namespace ne
{
    MIDIInDeviceMacOSX::MIDIInDeviceMacOSX(const uint32_t id, const std::string &name)
        : MIDIInDevice(id, name),
          mClient(0),
          mSource(0),
          mInPort(0)
    {
        _create();
    }

    bool MIDIInDeviceMacOSX::open()
    {
        if (isOpen()) return true;

        Log::Out("MIDIGetNumberOfDevices[%d]", MIDIGetNumberOfDevices());
        for (uint32_t d=0; d<MIDIGetNumberOfDevices(); ++d)
        {
            Log::Out("Device[%d]", d);
            MIDIDeviceRef device = MIDIGetDevice(d);
            for (uint32_t e=0; e<MIDIDeviceGetNumberOfEntities(device); ++e)
            {
                Log::Out("    Entity[%d]", e);
                MIDIEntityRef entity = MIDIDeviceGetEntity(device, e);
                for (uint32_t i=0; i<MIDIEntityGetNumberOfSources(entity); ++i)
                {
                    Log::Out("        Source[%d]", i);
                    mSource = MIDIEntityGetSource(entity, i);
                }
            }
        }

        CFStringRef deviceName = CFStringCreateWithCString(0, "MIDI In Device Client", kCFStringEncodingASCII);
        OSStatus status = MIDIClientCreate(deviceName, NotifyCallback, this, &mClient);
        CFRelease(deviceName);

        CFStringRef inName = CFStringCreateWithCString(0, "MIDI In Device InPort", kCFStringEncodingASCII);
        status = MIDIInputPortCreate(mClient, inName, ReadCallback, this, &mInPort);
        status = MIDIPortConnectSource(mInPort, mSource, this);
        CFRelease(inName);

        mIsOpen = true;

        return true;
    }

    bool MIDIInDeviceMacOSX::reset()
    {
        OSStatus status = MIDIRestart();
        if (status)
        {
            Log::Out(LL_ERROR, "MIDIInDeviceMacOSX::reset");
            return false;
        }
        return true;
    }

    bool MIDIInDeviceMacOSX::close()
    {
        if (!isOpen()) return true;

        MIDIPortDisconnectSource(mInPort, mSource);
        MIDIPortDispose(mInPort);
        MIDIClientDispose(mClient);

        mIsOpen = false;
        return true;
    }

    bool MIDIInDeviceMacOSX::receivedEvent()
    {
        Log::Out("MIDIInDeviceMacOSX::receivedEvent");
        MIDIPacketList packetList;
        OSStatus status = MIDIReceived(mSource, &packetList);
        if (status)
        {
            Log::Out(LL_ERROR, "MIDIInDeviceMacOSX::receivedEvent");
        }

        return true;
    }

    MIDIInDeviceMacOSX::~MIDIInDeviceMacOSX()
    {
        _destroy();
    }

    
    bool MIDIInDeviceMacOSX::_create()
    {
        return true;
    }

    bool MIDIInDeviceMacOSX::_destroy()
    {
        return close();
    }

    void MIDIInDeviceMacOSX::NotifyCallback(const MIDINotification *message, void *refCon)
    {
        Log::Out("MIDIInDeviceMacOSX::NotifyCallback");
    }

    void MIDIInDeviceMacOSX::ReadCallback(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon)
    {
        Log::Out("MIDIInDeviceMacOSX::ReadCallback");
        MIDIInDeviceMacOSX *device = static_cast<MIDIInDeviceMacOSX*>(readProcRefCon);
        if (!device)
        {
            Log::Out(LL_ERROR, "MIDIInDeviceMacOSX::ReadCallback");
        }
        for (uint32_t i=0; i<pktlist->numPackets; ++i)
        {
            Log::Out("MIDIInDeviceMacOSX::ReadCallback Packet[%d, %lf]", i, pktlist->packet[i].timeStamp);
            Log::Out("Data: [%d][%s]", pktlist->packet[i].length, pktlist->packet[i].data);
            for (uint32_t l=0; l<pktlist->packet[i].length; ++l)
            {
                Log::Out("0x%x", pktlist->packet[i].data[l]);
            }
        }
    }

    void MIDIInDeviceMacOSX::CompletionCallback(MIDISysexSendRequest *request)
    {
        Log::Out("MIDIInDeviceMacOSX::CompletionCallback");
    }
}

#endif
